home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 151 / cd-rom 151.iso / internet / firefox / Firefox Setup 3.0 Beta 1.exe / nonlocalized / components / nsBlocklistService.js < prev    next >
Encoding:
Text File  |  2007-11-09  |  21.3 KB  |  631 lines

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3. //@line 40 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\mozapps\extensions\src\nsBlocklistService.js"
  4. */
  5.  
  6. const Cc = Components.classes;
  7. const Ci = Components.interfaces;
  8. const Cr = Components.results;
  9.  
  10. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  11.  
  12. const kELEMENT_NODE                   = Ci.nsIDOMNode.ELEMENT_NODE;
  13. const TOOLKIT_ID                      = "toolkit@mozilla.org"
  14. const KEY_PROFILEDIR                  = "ProfD";
  15. const FILE_BLOCKLIST                  = "blocklist.xml";
  16. const PREF_BLOCKLIST_URL              = "extensions.blocklist.url";
  17. const PREF_BLOCKLIST_ENABLED          = "extensions.blocklist.enabled";
  18. const PREF_BLOCKLIST_INTERVAL         = "extensions.blocklist.interval";
  19. const PREF_EM_LOGGING_ENABLED         = "extensions.logging.enabled";
  20. const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
  21. const XMLURI_PARSE_ERROR              = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
  22.  
  23. const MODE_RDONLY   = 0x01;
  24. const MODE_WRONLY   = 0x02;
  25. const MODE_CREATE   = 0x08;
  26. const MODE_APPEND   = 0x10;
  27. const MODE_TRUNCATE = 0x20;
  28.  
  29. const PERMS_FILE      = 0644;
  30. const PERMS_DIRECTORY = 0755;
  31.  
  32. var gApp = null;
  33. var gPref = null;
  34. var gOS = null;
  35. var gConsole = null;
  36. var gVersionChecker = null;
  37. var gLoggingEnabled = null;
  38.  
  39. // shared code for suppressing bad cert dialogs
  40. //@line 40 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\mozapps\shared\src\badCertHandler.js"
  41.  
  42. /**
  43.  * Only allow built-in certs for HTTPS connections.  See bug 340198.
  44.  */
  45. function checkCert(channel) {
  46.   if (!channel.originalURI.schemeIs("https"))  // bypass
  47.     return;
  48.  
  49.   const Ci = Components.interfaces;  
  50.   var cert =
  51.       channel.securityInfo.QueryInterface(Ci.nsISSLStatusProvider).
  52.       SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
  53.  
  54.   var issuer = cert.issuer;
  55.   while (issuer && !cert.equals(issuer)) {
  56.     cert = issuer;
  57.     issuer = cert.issuer;
  58.   }
  59.  
  60.   if (!issuer || issuer.tokenName != "Builtin Object Token")
  61.     throw "cert issuer is not built-in";
  62. }
  63.  
  64. /**
  65.  * This class implements nsIBadCertListener.  It's job is to prevent "bad cert"
  66.  * security dialogs from being shown to the user.  It is better to simply fail
  67.  * if the certificate is bad. See bug 304286.
  68.  */
  69. function BadCertHandler() {
  70. }
  71. BadCertHandler.prototype = {
  72.  
  73.   // nsIChannelEventSink
  74.   onChannelRedirect: function(oldChannel, newChannel, flags) {
  75.     // make sure the certificate of the old channel checks out before we follow
  76.     // a redirect from it.  See bug 340198.
  77.     checkCert(oldChannel);
  78.   },
  79.  
  80.   // nsIInterfaceRequestor
  81.   getInterface: function(iid) {
  82.     return this.QueryInterface(iid);
  83.   },
  84.  
  85.   // nsISupports
  86.   QueryInterface: function(iid) {
  87.     if (!iid.equals(Components.interfaces.nsIChannelEventSink) &&
  88.         !iid.equals(Components.interfaces.nsIInterfaceRequestor) &&
  89.         !iid.equals(Components.interfaces.nsISupports))
  90.       throw Components.results.NS_ERROR_NO_INTERFACE;
  91.     return this;
  92.   }
  93. };
  94. //@line 77 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\mozapps\extensions\src\nsBlocklistService.js"
  95.  
  96. /**
  97.  * Logs a string to the error console.
  98.  * @param   string
  99.  *          The string to write to the error console..
  100.  */
  101. function LOG(string) {
  102.   if (gLoggingEnabled) {
  103.     dump("*** " + string + "\n");
  104.     if (gConsole)
  105.       gConsole.logStringMessage(string);
  106.   }
  107. }
  108.  
  109. /**
  110.  * Gets a preference value, handling the case where there is no default.
  111.  * @param   func
  112.  *          The name of the preference function to call, on nsIPrefBranch
  113.  * @param   preference
  114.  *          The name of the preference
  115.  * @param   defaultValue
  116.  *          The default value to return in the event the preference has
  117.  *          no setting
  118.  * @returns The value of the preference, or undefined if there was no
  119.  *          user or default value.
  120.  */
  121. function getPref(func, preference, defaultValue) {
  122.   try {
  123.     return gPref[func](preference);
  124.   }
  125.   catch (e) {
  126.   }
  127.   return defaultValue;
  128. }
  129.  
  130. /**
  131.  * Gets the file at the specified hierarchy under a Directory Service key.
  132.  * @param   key
  133.  *          The Directory Service Key to start from
  134.  * @param   pathArray
  135.  *          An array of path components to locate beneath the directory
  136.  *          specified by |key|. The last item in this array must be the
  137.  *          leaf name of a file.
  138.  * @return  nsIFile object for the file specified. The file is NOT created
  139.  *          if it does not exist, however all required directories along
  140.  *          the way are.
  141.  */
  142. function getFile(key, pathArray) {
  143.   var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
  144.                     getService(Ci.nsIProperties);
  145.   var file = fileLocator.get(key, Ci.nsILocalFile);
  146.   for (var i = 0; i < pathArray.length - 1; ++i) {
  147.     file.append(pathArray[i]);
  148.     if (!file.exists())
  149.       file.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
  150.   }
  151.   file.followLinks = false;
  152.   file.append(pathArray[pathArray.length - 1]);
  153.   return file;
  154. }
  155.  
  156. /**
  157.  * Opens a safe file output stream for writing.
  158.  * @param   file
  159.  *          The file to write to.
  160.  * @param   modeFlags
  161.  *          (optional) File open flags. Can be undefined.
  162.  * @returns nsIFileOutputStream to write to.
  163.  */
  164. function openSafeFileOutputStream(file, modeFlags) {
  165.   var fos = Cc["@mozilla.org/network/safe-file-output-stream;1"].
  166.             createInstance(Ci.nsIFileOutputStream);
  167.   if (modeFlags === undefined)
  168.     modeFlags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
  169.   if (!file.exists())
  170.     file.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
  171.   fos.init(file, modeFlags, PERMS_FILE, 0);
  172.   return fos;
  173. }
  174.  
  175. /**
  176.  * Closes a safe file output stream.
  177.  * @param   stream
  178.  *          The stream to close.
  179.  */
  180. function closeSafeFileOutputStream(stream) {
  181.   if (stream instanceof Ci.nsISafeOutputStream)
  182.     stream.finish();
  183.   else
  184.     stream.close();
  185. }
  186.  
  187. /**
  188.  * Constructs a URI to a spec.
  189.  * @param   spec
  190.  *          The spec to construct a URI to
  191.  * @returns The nsIURI constructed.
  192.  */
  193. function newURI(spec) {
  194.   var ioServ = Cc["@mozilla.org/network/io-service;1"].
  195.                getService(Ci.nsIIOService);
  196.   return ioServ.newURI(spec, null, null);
  197. }
  198.  
  199. /**
  200.  * Manages the Blocklist. The Blocklist is a representation of the contents of
  201.  * blocklist.xml and allows us to remotely disable / re-enable blocklisted
  202.  * items managed by the Extension Manager with an item's appDisabled property.
  203.  * It also blocklists plugins with data from blocklist.xml.
  204.  */
  205.  
  206. function Blocklist() {
  207.   gApp = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
  208.   gApp.QueryInterface(Ci.nsIXULRuntime);
  209.   gPref = Cc["@mozilla.org/preferences-service;1"].
  210.           getService(Ci.nsIPrefBranch2);
  211.   gVersionChecker = Cc["@mozilla.org/xpcom/version-comparator;1"].
  212.                     getService(Ci.nsIVersionComparator);
  213.   gConsole = Cc["@mozilla.org/consoleservice;1"].
  214.              getService(Ci.nsIConsoleService);
  215.  
  216.   gOS = Cc["@mozilla.org/observer-service;1"].
  217.         getService(Ci.nsIObserverService);
  218.   gOS.addObserver(this, "xpcom-shutdown", false);
  219. }
  220.  
  221. Blocklist.prototype = {
  222.   /**
  223.    * Extension ID -> array of Version Ranges
  224.    * Each value in the version range array is a JS Object that has the
  225.    * following properties:
  226.    *   "minVersion"  The minimum version in a version range (default = 0)
  227.    *   "maxVersion"  The maximum version in a version range (default = *)
  228.    *   "targetApps"  Application ID -> array of Version Ranges
  229.    *                 (default = current application ID)
  230.    *                 Each value in the version range array is a JS Object that
  231.    *                 has the following properties:
  232.    *                   "minVersion"  The minimum version in a version range
  233.    *                                 (default = 0)
  234.    *                   "maxVersion"  The maximum version in a version range
  235.    *                                 (default = *)
  236.    */
  237.   _addonEntries: null,
  238.   _pluginEntries: null,
  239.  
  240.   observe: function (aSubject, aTopic, aData) {
  241.     switch (aTopic) {
  242.     case "app-startup":
  243.       gOS.addObserver(this, "plugins-list-updated", false);
  244.       gOS.addObserver(this, "profile-after-change", false);
  245.       gOS.addObserver(this, "quit-application", false);
  246.       break;
  247.     case "profile-after-change":
  248.       gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
  249.       var tm = Cc["@mozilla.org/updates/timer-manager;1"].
  250.                getService(Ci.nsIUpdateTimerManager);
  251.       var interval = getPref("getIntPref", PREF_BLOCKLIST_INTERVAL, 86400);
  252.       tm.registerTimer("blocklist-background-update-timer", this, interval);
  253.       break;
  254.     case "plugins-list-updated":
  255.       this._checkPluginsList();
  256.       break;
  257.     case "quit-application":
  258.       gOS.removeObserver(this, "plugins-list-updated");
  259.       gOS.removeObserver(this, "profile-after-change");
  260.       gOS.removeObserver(this, "quit-application");
  261.       break;
  262.     case "xpcom-shutdown":
  263.       gOS.removeObserver(this, "xpcom-shutdown");
  264.       gOS = null;
  265.       gPref = null;
  266.       gConsole = null;
  267.       gVersionChecker = null;
  268.       gApp = null;
  269.       break;
  270.     }
  271.   },
  272.  
  273.   isAddonBlocklisted: function(id, version, appVersion, toolkitVersion) {
  274.     if (!this._addonEntries)
  275.       this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]));
  276.     if (appVersion === undefined)
  277.       appVersion = gApp.version;
  278.     if (toolkitVersion === undefined)
  279.       toolkitVersion = gApp.platformVersion;
  280.  
  281.     var blItem = this._addonEntries[id];
  282.     if (!blItem)
  283.       return false;
  284.  
  285.     for (var i = 0; i < blItem.length; ++i) {
  286.       if (gVersionChecker.compare(version, blItem[i].minVersion) < 0  ||
  287.           gVersionChecker.compare(version, blItem[i].maxVersion) > 0)
  288.         continue;
  289.  
  290.       var blTargetApp = blItem[i].targetApps[gApp.ID];
  291.       if (blTargetApp) {
  292.         for (var x = 0; x < blTargetApp.length; ++x) {
  293.           if (gVersionChecker.compare(appVersion, blTargetApp[x].minVersion) < 0 ||
  294.               gVersionChecker.compare(appVersion, blTargetApp[x].maxVersion) > 0)
  295.             continue;
  296.           return true;
  297.         }
  298.       }
  299.  
  300.       blTargetApp = blItem[i].targetApps[TOOLKIT_ID];
  301.       if (!blTargetApp)
  302.         return false;
  303.       for (x = 0; x < blTargetApp.length; ++x) {
  304.         if (gVersionChecker.compare(toolkitVersion, blTargetApp[x].minVersion) < 0 ||
  305.             gVersionChecker.compare(toolkitVersion, blTargetApp[x].maxVersion) > 0)
  306.           continue;
  307.         return true;
  308.       }
  309.     }
  310.     return false;
  311.   },
  312.  
  313.   notify: function(aTimer) {
  314.     if (getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true) == false)
  315.       return;
  316.  
  317.     try {
  318.       var dsURI = gPref.getCharPref(PREF_BLOCKLIST_URL);
  319.     }
  320.     catch (e) {
  321.       LOG("Blocklist::notify: The " + PREF_BLOCKLIST_URL + " preference" +
  322.           " is missing!");
  323.       return;
  324.     }
  325.  
  326.     dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID);
  327.     dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version);
  328.     // Verify that the URI is valid
  329.     try {
  330.       var uri = newURI(dsURI);
  331.     }
  332.     catch (e) {
  333.       LOG("Blocklist::notify: There was an error creating the blocklist URI\r\n" +
  334.           "for: " + dsURI + ", error: " + e);
  335.       return;
  336.     }
  337.  
  338.     var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
  339.                   createInstance(Ci.nsIXMLHttpRequest);
  340.     request.open("GET", uri.spec, true);
  341.     request.channel.notificationCallbacks = new BadCertHandler();
  342.     request.overrideMimeType("text/xml");
  343.     request.setRequestHeader("Cache-Control", "no-cache");
  344.     request.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
  345.  
  346.     var self = this;
  347.     request.onerror = function(event) { self.onXMLError(event); };
  348.     request.onload  = function(event) { self.onXMLLoad(event);  };
  349.     request.send(null);
  350.   },
  351.  
  352.   onXMLLoad: function(aEvent) {
  353.     var request = aEvent.target;
  354.     try {
  355.       checkCert(request.channel);
  356.     }
  357.     catch (e) {
  358.       LOG("Blocklist::onXMLLoad: " + e);
  359.       return;
  360.     }
  361.     var responseXML = request.responseXML;
  362.     if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
  363.         (request.status != 200 && request.status != 0)) {
  364.       LOG("Blocklist::onXMLLoad: there was an error during load");
  365.       return;
  366.     }
  367.     var blocklistFile = getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
  368.     if (blocklistFile.exists())
  369.       blocklistFile.remove(false);
  370.     var fos = openSafeFileOutputStream(blocklistFile);
  371.     fos.write(request.responseText, request.responseText.length);
  372.     closeSafeFileOutputStream(fos);
  373.     this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]));
  374.     var em = Cc["@mozilla.org/extensions/manager;1"].
  375.              getService(Ci.nsIExtensionManager);
  376.     em.checkForBlocklistChanges();
  377.     this._checkPluginsList();
  378.   },
  379.  
  380.   onXMLError: function(aEvent) {
  381.     try {
  382.       var request = aEvent.target;
  383.       // the following may throw (e.g. a local file or timeout)
  384.       var status = request.status;
  385.     }
  386.     catch (e) {
  387.       request = aEvent.target.channel.QueryInterface(Ci.nsIRequest);
  388.       status = request.status;
  389.     }
  390.     var statusText = request.statusText;
  391.     // When status is 0 we don't have a valid channel.
  392.     if (status == 0)
  393.       statusText = "nsIXMLHttpRequest channel unavailable";
  394.     LOG("Blocklist:onError: There was an error loading the blocklist file\r\n" +
  395.         statusText);
  396.   },
  397.  
  398.   /**
  399. //@line 430 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\mozapps\extensions\src\nsBlocklistService.js"
  400.    */
  401.  
  402.   _loadBlocklistFromFile: function(file) {
  403.     this._addonEntries = { };
  404.     this._pluginEntries = { };
  405.     if (getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true) == false) {
  406.       LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
  407.       return;
  408.     }
  409.  
  410.     if (!file.exists()) {
  411.       LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist");
  412.       return;
  413.     }
  414.  
  415.     var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
  416.                                .createInstance(Components.interfaces.nsIFileInputStream);
  417.     fileStream.init(file, MODE_RDONLY, PERMS_FILE, 0);
  418.     try {
  419.       var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
  420.                    createInstance(Ci.nsIDOMParser);
  421.       var doc = parser.parseFromStream(fileStream, "UTF-8", file.fileSize, "text/xml");
  422.       if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
  423.         LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
  424.             "XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
  425.             "Received: " + doc.documentElement.namespaceURI);
  426.         return;
  427.       }
  428.  
  429.       var childNodes = doc.documentElement.childNodes;
  430.       this._addonEntries = this._processItemNodes(childNodes, "em",
  431.                                             this._handleEmItemNode);
  432.       this._pluginEntries = this._processItemNodes(childNodes, "plugin",
  433.                                                    this._handlePluginItemNode);
  434.     }
  435.     catch (e) {
  436.       LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
  437.       return;
  438.     }
  439.     fileStream.close();
  440.   },
  441.  
  442.   _processItemNodes: function(deChildNodes, prefix, handler) {
  443.     var result = [];
  444.     var itemNodes;
  445.     var containerName = prefix + "Items";
  446.     for (var i = 0; i < deChildNodes.length; ++i) {
  447.       var emItemsElement = deChildNodes[i];
  448.       if (emItemsElement.nodeType == kELEMENT_NODE &&
  449.           emItemsElement.localName == containerName) {
  450.         itemNodes = emItemsElement.childNodes;
  451.         break;
  452.       }
  453.     }
  454.     if (!itemNodes)
  455.       return result;
  456.  
  457.     var itemName = prefix + "Item";
  458.     for (var i = 0; i < itemNodes.length; ++i) {
  459.       var blocklistElement = itemNodes[i];
  460.       if (blocklistElement.nodeType != kELEMENT_NODE ||
  461.           blocklistElement.localName != itemName)
  462.         continue;
  463.  
  464.       blocklistElement.QueryInterface(Ci.nsIDOMElement);
  465.       handler(blocklistElement, result);
  466.     }
  467.     return result;
  468.   },
  469.  
  470.   _handleEmItemNode: function(blocklistElement, result) {
  471.     var versionNodes = blocklistElement.childNodes;
  472.     var id = blocklistElement.getAttribute("id");
  473.     result[id] = [];
  474.     for (var x = 0; x < versionNodes.length; ++x) {
  475.       var versionRangeElement = versionNodes[x];
  476.       if (versionRangeElement.nodeType != kELEMENT_NODE ||
  477.           versionRangeElement.localName != "versionRange")
  478.         continue;
  479.  
  480.       result[id].push(new BlocklistItemData(versionRangeElement));
  481.     }
  482.     // if only the extension ID is specified block all versions of the
  483.     // extension for the current application.
  484.     if (result[id].length == 0)
  485.       result[id].push(new BlocklistItemData(null));
  486.   },
  487.  
  488.   _handlePluginItemNode: function(blocklistElement, result) {
  489.     var matchNodes = blocklistElement.childNodes;
  490.     var matchList;
  491.     for (var x = 0; x < matchNodes.length; ++x) {
  492.       var matchElement = matchNodes[x];
  493.       if (matchElement.nodeType != kELEMENT_NODE ||
  494.           matchElement.localName != "match")
  495.         continue;
  496.  
  497.       var name = matchElement.getAttribute("name");
  498.       var exp = matchElement.getAttribute("exp");
  499.       if (!matchList)
  500.         matchList = { };
  501.       matchList[name] = new RegExp(exp, "m");
  502.     }
  503.     if (matchList)
  504.       result.push(matchList);
  505.   },
  506.  
  507.   _checkPlugin: function(plugin) {
  508.     for each (var matchList in this._pluginEntries) {
  509.       var matchFailed = false;
  510.       for (var name in matchList) {
  511.         if (typeof(plugin[name]) != "string" ||
  512.             !matchList[name].test(plugin[name])) {
  513.           matchFailed = true;
  514.           break;
  515.         }
  516.       }
  517.  
  518.       if (!matchFailed) {
  519.         plugin.blocklisted = true;
  520.         return;
  521.       }
  522.     }
  523.     plugin.blocklisted = false;
  524.   },
  525.  
  526.   _checkPluginsList: function() {
  527.     if (!this._addonEntries)
  528.       this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]));
  529.     var phs = Cc["@mozilla.org/plugin/host;1"].
  530.               getService(Ci.nsIPluginHost);
  531.     phs.getPluginTags({ }).forEach(this._checkPlugin, this);
  532.   },
  533.  
  534.   classDescription: "Blocklist Service",
  535.   contractID: "@mozilla.org/extensions/blocklist;1",
  536.   classID: Components.ID("{66354bc9-7ed1-4692-ae1d-8da97d6b205e}"),
  537.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
  538.                                          Ci.nsIBlocklistService,
  539.                                          Ci.nsITimerCallback]),
  540.   _xpcom_categories: [{
  541.     category: "app-startup",
  542.     service: true
  543.   }]
  544. };
  545.  
  546. /**
  547.  * Helper for constructing a blocklist.
  548.  */
  549. function BlocklistItemData(versionRangeElement) {
  550.   var versionRange = this.getBlocklistVersionRange(versionRangeElement);
  551.   this.minVersion = versionRange.minVersion;
  552.   this.maxVersion = versionRange.maxVersion;
  553.   this.targetApps = { };
  554.   var found = false;
  555.  
  556.   if (versionRangeElement) {
  557.     for (var i = 0; i < versionRangeElement.childNodes.length; ++i) {
  558.       var targetAppElement = versionRangeElement.childNodes[i];
  559.       if (targetAppElement.nodeType != Ci.nsIDOMNode.ELEMENT_NODE ||
  560.           targetAppElement.localName != "targetApplication")
  561.         continue;
  562.       found = true;
  563.       // default to the current application if id is not provided.
  564.       var appID = targetAppElement.hasAttribute("id") ? targetAppElement.getAttribute("id") : gApp.ID;
  565.       this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement);
  566.     }
  567.   }
  568.   // Default to all versions of the extension and the current application when
  569.   // versionRange is not defined.
  570.   if (!found)
  571.     this.targetApps[gApp.ID] = this.getBlocklistAppVersions(null);
  572. }
  573.  
  574. BlocklistItemData.prototype = {
  575. /**
  576.  * Retrieves a version range (e.g. minVersion and maxVersion) for a
  577.  * blocklist item's targetApplication element.
  578.  * @param   targetAppElement
  579.  *          A targetApplication blocklist element.
  580.  * @returns An array of JS objects with the following properties:
  581.  *          "minVersion"  The minimum version in a version range (default = 0).
  582.  *          "maxVersion"  The maximum version in a version range (default = *).
  583.  */
  584.   getBlocklistAppVersions: function(targetAppElement) {
  585.     var appVersions = [ ];
  586.     var found = false;
  587.  
  588.     if (targetAppElement) {
  589.       for (var i = 0; i < targetAppElement.childNodes.length; ++i) {
  590.         var versionRangeElement = targetAppElement.childNodes[i];
  591.         if (versionRangeElement.nodeType != Ci.nsIDOMNode.ELEMENT_NODE ||
  592.             versionRangeElement.localName != "versionRange")
  593.           continue;
  594.         found = true;
  595.         appVersions.push(this.getBlocklistVersionRange(versionRangeElement));
  596.       }
  597.     }
  598.     // return minVersion = 0 and maxVersion = * if not available
  599.     if (!found)
  600.       return [ this.getBlocklistVersionRange(null) ];
  601.     return appVersions;
  602.   },
  603.  
  604. /**
  605.  * Retrieves a version range (e.g. minVersion and maxVersion) for a blocklist
  606.  * versionRange element.
  607.  * @param   versionRangeElement
  608.  *          The versionRange blocklist element.
  609.  * @returns A JS object with the following properties:
  610.  *          "minVersion"  The minimum version in a version range (default = 0).
  611.  *          "maxVersion"  The maximum version in a version range (default = *).
  612.  */
  613.   getBlocklistVersionRange: function(versionRangeElement) {
  614.     var minVersion = "0";
  615.     var maxVersion = "*";
  616.     if (!versionRangeElement)
  617.       return { minVersion: minVersion, maxVersion: maxVersion };
  618.  
  619.     if (versionRangeElement.hasAttribute("minVersion"))
  620.       minVersion = versionRangeElement.getAttribute("minVersion");
  621.     if (versionRangeElement.hasAttribute("maxVersion"))
  622.       maxVersion = versionRangeElement.getAttribute("maxVersion");
  623.  
  624.     return { minVersion: minVersion, maxVersion: maxVersion };
  625.   }
  626. };
  627.  
  628. function NSGetModule(aCompMgr, aFileSpec) {
  629.   return XPCOMUtils.generateModule([Blocklist]);
  630. }
  631.